/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id: FilterExpr.java,v 1.2.4.1 2005/09/12 10:22:50 pvedula Exp $
 */

package com.sun.org.apache.xalan.internal.xsltc.compiler;

import java.util.Vector;

import com.sun.org.apache.bcel.internal.generic.ALOAD;
import com.sun.org.apache.bcel.internal.generic.ASTORE;
import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
import com.sun.org.apache.bcel.internal.generic.ILOAD;
import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
import com.sun.org.apache.bcel.internal.generic.InstructionList;
import com.sun.org.apache.bcel.internal.generic.ISTORE;
import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;

/**
 * @author Jacek Ambroziak
 * @author Santiago Pericas-Geertsen
 * @author Morten Jorgensen
 */
class FilterExpr extends Expression {

  /**
   * Primary expression of this filter. I.e., 'e' in '(e)[p1]...[pn]'.
   */
  private Expression _primary;

  /**
   * Array of predicates in '(e)[p1]...[pn]'.
   */
  private final Vector _predicates;

  public FilterExpr(Expression primary, Vector predicates) {
    _primary = primary;
    _predicates = predicates;
    primary.setParent(this);
  }

  protected Expression getExpr() {
    if (_primary instanceof CastExpr) {
      return ((CastExpr) _primary).getExpr();
    } else {
      return _primary;
    }
  }

  public void setParser(Parser parser) {
    super.setParser(parser);
    _primary.setParser(parser);
    if (_predicates != null) {
      final int n = _predicates.size();
      for (int i = 0; i < n; i++) {
        final Expression exp = (Expression) _predicates.elementAt(i);
        exp.setParser(parser);
        exp.setParent(this);
      }
    }
  }

  public String toString() {
    return "filter-expr(" + _primary + ", " + _predicates + ")";
  }

  /**
   * Type check a FilterParentPath. If the filter is not a node-set add a
   * cast to node-set only if it is of reference type. This type coercion
   * is needed for expressions like $x where $x is a parameter reference.
   * All optimizations are turned off before type checking underlying
   * predicates.
   */
  public Type typeCheck(SymbolTable stable) throws TypeCheckError {
    Type ptype = _primary.typeCheck(stable);
    boolean canOptimize = _primary instanceof KeyCall;

    if (ptype instanceof NodeSetType == false) {
      if (ptype instanceof ReferenceType) {
        _primary = new CastExpr(_primary, Type.NodeSet);
      } else {
        throw new TypeCheckError(this);
      }
    }

    // Type check predicates and turn all optimizations off if appropriate
    int n = _predicates.size();
    for (int i = 0; i < n; i++) {
      Predicate pred = (Predicate) _predicates.elementAt(i);

      if (!canOptimize) {
        pred.dontOptimize();
      }
      pred.typeCheck(stable);
    }
    return _type = Type.NodeSet;
  }

  /**
   * Translate a filter expression by pushing the appropriate iterator
   * onto the stack.
   */
  public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
    translateFilterExpr(classGen, methodGen, _predicates == null ? -1 : _predicates.size() - 1);
  }

  private void translateFilterExpr(ClassGenerator classGen,
      MethodGenerator methodGen,
      int predicateIndex) {
    if (predicateIndex >= 0) {
      translatePredicates(classGen, methodGen, predicateIndex);
    } else {
      _primary.translate(classGen, methodGen);
    }
  }

  /**
   * Translate a sequence of predicates. Each predicate is translated
   * by constructing an instance of <code>CurrentNodeListIterator</code>
   * which is initialized from another iterator (recursive call), a
   * filter and a closure (call to translate on the predicate) and "this".
   */
  public void translatePredicates(ClassGenerator classGen,
      MethodGenerator methodGen,
      int predicateIndex) {
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = methodGen.getInstructionList();

    // If not predicates left, translate primary expression
    if (predicateIndex < 0) {
      translateFilterExpr(classGen, methodGen, predicateIndex);
    } else {
      // Get the next predicate to be translated
      Predicate predicate = (Predicate) _predicates.get(predicateIndex--);

      // Translate the rest of the predicates from right to left
      translatePredicates(classGen, methodGen, predicateIndex);

      if (predicate.isNthPositionFilter()) {
        int nthIteratorIdx = cpg.addMethodref(NTH_ITERATOR_CLASS,
            "<init>",
            "(" + NODE_ITERATOR_SIG + "I)V");

        // Backwards branches are prohibited if an uninitialized object
        // is on the stack by section 4.9.4 of the JVM Specification,
        // 2nd Ed.  We don't know whether this code might contain
        // backwards branches, so we mustn't create the new object unti

        // after we've created the suspect arguments to its constructor

        // Instead we calculate the values of the arguments to the
        // constructor first, store them in temporary variables, create
        // the object and reload the arguments from the temporaries to
        // avoid the problem.
        LocalVariableGen iteratorTemp
            = methodGen.addLocalVariable("filter_expr_tmp1",
            Util.getJCRefType(NODE_ITERATOR_SIG),
            null, null);
        iteratorTemp.setStart(
            il.append(new ASTORE(iteratorTemp.getIndex())));

        predicate.translate(classGen, methodGen);
        LocalVariableGen predicateValueTemp
            = methodGen.addLocalVariable("filter_expr_tmp2",
            Util.getJCRefType("I"),
            null, null);
        predicateValueTemp.setStart(
            il.append(new ISTORE(predicateValueTemp.getIndex())));

        il.append(new NEW(cpg.addClass(NTH_ITERATOR_CLASS)));
        il.append(DUP);
        iteratorTemp.setEnd(
            il.append(new ALOAD(iteratorTemp.getIndex())));
        predicateValueTemp.setEnd(
            il.append(new ILOAD(predicateValueTemp.getIndex())));
        il.append(new INVOKESPECIAL(nthIteratorIdx));
      } else {
        // Translate predicates from right to left
        final int initCNLI = cpg.addMethodref(CURRENT_NODE_LIST_ITERATOR,
            "<init>",
            "(" + NODE_ITERATOR_SIG + "Z" +
                CURRENT_NODE_LIST_FILTER_SIG +
                NODE_SIG + TRANSLET_SIG + ")V");

        // Backwards branches are prohibited if an uninitialized object is
        // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
        // We don't know whether this code might contain backwards branches,
        // so we mustn't create the new object until after we've created
        // the suspect arguments to its constructor.  Instead we calculate
        // the values of the arguments to the constructor first, store them
        // in temporary variables, create the object and reload the
        // arguments from the temporaries to avoid the problem.

        LocalVariableGen nodeIteratorTemp =
            methodGen.addLocalVariable("filter_expr_tmp1",
                Util.getJCRefType(NODE_ITERATOR_SIG),
                null, null);
        nodeIteratorTemp.setStart(
            il.append(new ASTORE(nodeIteratorTemp.getIndex())));

        predicate.translate(classGen, methodGen);
        LocalVariableGen filterTemp =
            methodGen.addLocalVariable("filter_expr_tmp2",
                Util.getJCRefType(CURRENT_NODE_LIST_FILTER_SIG),
                null, null);
        filterTemp.setStart(il.append(new ASTORE(filterTemp.getIndex())));

        // Create a CurrentNodeListIterator
        il.append(new NEW(cpg.addClass(CURRENT_NODE_LIST_ITERATOR)));
        il.append(DUP);

        // Initialize CurrentNodeListIterator
        nodeIteratorTemp.setEnd(
            il.append(new ALOAD(nodeIteratorTemp.getIndex())));
        il.append(ICONST_1);
        filterTemp.setEnd(il.append(new ALOAD(filterTemp.getIndex())));
        il.append(methodGen.loadCurrentNode());
        il.append(classGen.loadTranslet());
        il.append(new INVOKESPECIAL(initCNLI));
      }
    }
  }
}
